#include "Windows/GxThreads.h"
#include "Basic/Exceptions.h"
#include <process.h>
namespace GxWinSystem
{
namespace Threading
{
	class gxThreadException : public Exception
	{};
	class gxThreadCreationException : public Exception
	{};



	unsigned int __stdcall ThreadProc(void * param)
	{
		gxThreadParam * p = (gxThreadParam *) param;
		if (p->thread->HasParameter())
			p->thread->paramedThreadProc->Invoke(p->threadParam);
		else
			p->thread->threadProc->Invoke();
		_endthreadex(0);
		return 0;
	}

	gxThread::gxThread(gxThreadProc * p)
	{
		threadProc = p;
		internalParam.thread = this;
		threadHandle = (HANDLE)_beginthreadex(0,0, ThreadProc, &internalParam, CREATE_SUSPENDED, 0);
		if (threadHandle == 0)
			throw gxThreadCreationException();
	}
	gxThread::gxThread(gxThreadParameterizedProc * p, Object * param)
	{
		paramedThreadProc = p;
		internalParam.thread = this;
		internalParam.threadParam = param;
		threadHandle = (HANDLE)_beginthreadex(0,0, ThreadProc, &internalParam, CREATE_SUSPENDED, 0);
		if (threadHandle == 0)
			throw gxThreadCreationException();
	}
	gxThread::~gxThread()
	{
		
		CloseHandle(threadHandle);
	}
	HANDLE gxThread::GetHandle()
	{
		return threadHandle;
	}
	bool gxThread::HasParameter()
	{
		return paramedThreadProc;
	}
	void gxThread::Start()
	{
		ResumeThread(threadHandle);
	}
	void gxThread::Suspend()
	{
		SuspendThread(threadHandle);
	}
	void gxThread::Terminate()
	{
		DWORD exit = 0;
		if (GetExitCodeThread(threadHandle, &exit)!=0)
			ExitThread(exit);
		else
			throw gxThreadException();
	}
	void gxThread::SetPriority(gxThread::gxThreadPriority p)
	{
		int prior;
		switch (p)
		{
		case gxtpNormal:
			prior = THREAD_PRIORITY_NORMAL;
			break;
		case gxtpAboveNormal:
			prior = THREAD_PRIORITY_ABOVE_NORMAL;
			break;
		case gxtpHighest:
			prior = THREAD_PRIORITY_HIGHEST;
			break;
		case gxtpCritical:
			prior = THREAD_PRIORITY_TIME_CRITICAL;
			break;
		case gxtpBelowNormal:
			prior = THREAD_PRIORITY_BELOW_NORMAL;
			break;
		case gxtpLowest:
			prior = THREAD_PRIORITY_LOWEST;
			break;
		default:
			prior = THREAD_PRIORITY_IDLE;
		}
		SetThreadPriority(threadHandle, prior);
	}
	gxThread::gxThreadPriority gxThread::GetPriority()
	{
		int prior;
		prior = GetThreadPriority(threadHandle);
		switch (prior)
		{
		case THREAD_PRIORITY_NORMAL:
			return gxtpNormal;
			break;
		case THREAD_PRIORITY_ABOVE_NORMAL:
			return gxtpAboveNormal;
		case THREAD_PRIORITY_HIGHEST:
			return gxtpHighest;
		case THREAD_PRIORITY_TIME_CRITICAL:
			return gxtpCritical;
		case THREAD_PRIORITY_BELOW_NORMAL:
			return gxtpBelowNormal;
		case THREAD_PRIORITY_LOWEST:
			return gxtpLowest;
		default:
			return gxtpIdle;
		}
	}

	gxLock::gxLock()
	{
		InitializeCriticalSection(&handle);
	}

	gxLock::~gxLock()
	{
		DeleteCriticalSection(&handle);
	}

	void gxLock::Lock()
	{
		EnterCriticalSection(&handle);
	}

	void gxLock::Unlock()
	{
		LeaveCriticalSection(&handle);
	}

	bool gxLock::TryLock()
	{
		return TryEnterCriticalSection(&handle)!=0;
	}
}
}